制造商需要提炼几种原料油,并将它们混合在一起,才能生产出一种可出售的食品。所需生油分为两类:
Category | Oil |
---|---|
Vegetable oils: | VEG 1 VEG 2 |
Non-vegetable oils: | OIL 1 OIL 2 OIL 3 |
制造商可以选择购买当月的原料油和/或在期货市场上购买并在下一个月交付。即期交割和期货市场的价格以美元/吨计算如下:
Month | VEG 1 | VEG 2 | OIL 1 | OIL 2 | OIL 3 |
---|---|---|---|---|---|
January | 110 | 120 | 130 | 110 | 115 |
February | 130 | 130 | 110 | 90 | 115 |
March | 110 | 140 | 130 | 100 | 95 |
April | 120 | 110 | 120 | 120 | 125 |
May | 100 | 120 | 150 | 110 | 105 |
June | 90 | 100 | 140 | 80 | 135 |
还有一些其他的因素必须加以考虑。这些包括:
除了上面提到的炼油限制之外,还有储存以供未来使用的原料油数量的限制,每储存一吨油都有成本。每种原料油限量1000吨,储存费为每月每吨$5$美元。生产企业不得储存生产出来的食品或成品油。
最终的食品产品的硬度必须在3到6之间的一个给定的硬度等级。在模型中,硬度线性混合,各原料油的硬度为:
Oils | Hardness |
---|---|
VEG 1 | 8.8 |
VEG 2 | 6.1 |
OIL 1 | 2.0 |
OIL2 | 4.2 |
OIL 3 | 5.0 |
在1月初,每种原料油有500吨储存。同样地,原油在6月底每种原料油也需要有500吨存储。
根据上述信息,每月应该做出什么样的购买和制造决策,以实现利润最大化?
初始存储量: 一月份油$o$的购买量+初始存储量等于这个月油的消费量+存储量 $$ \begin{equation} \text{init_store} + \text{buy}_{Jan, o} = \text{consume}_{Jan,o} + \text{store}_{Jan,o} \quad \forall o \in \text{Oils} \tag{1} \end{equation} $$
平衡: 上个月的存储量+购买量=这个月的消耗量+存储量 $$ \begin{equation} \text{store}_{t-1,o} + \text{buy}_{t,o} = \text{consume}_{t,o} + \text{store}_{t,o} \quad \forall (t,o) \in \text{Months} \setminus \{\text{Jan}\} \times \text{Oils} \tag{2} \end{equation} $$
存储目标: 最后一个月的存储量要达到目标 $$ \begin{equation} \text{store}_{Jun,o} = \text{target_store} \quad \forall o \in \text{Oils} \tag{3} \end{equation} $$
硬度: 成品油硬度值应满足硬度要求 $$ \begin{equation} \text{min_hardness}*\text{produce}_t \leq \sum_{o \in \text{Oils}} \text{hardness}_o*\text{consume}_{t,o} \leq \text{max_hardness}*\text{produce}_t \quad \forall t \in \text{Months} \tag{5} \end{equation} $$
质量守恒:消耗的原料油的量等于产出的成品油的量 $$ \begin{equation} \sum_{o \in \text{Oils}}\text{consume}_{t,o} = \text{produce}_t \quad \forall t \in \text{Months} \tag{6} \end{equation} $$
参考资料:
import numpy as np
import pandas as pd
import pulp as lp
# Parameters
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
oils = ["VEG1", "VEG2", "OIL1", "OIL2", "OIL3"]
cost = {
('Jan', 'VEG1'): 110,
('Jan', 'VEG2'): 120,
('Jan', 'OIL1'): 130,
('Jan', 'OIL2'): 110,
('Jan', 'OIL3'): 115,
('Feb', 'VEG1'): 130,
('Feb', 'VEG2'): 130,
('Feb', 'OIL1'): 110,
('Feb', 'OIL2'): 90,
('Feb', 'OIL3'): 115,
('Mar', 'VEG1'): 110,
('Mar', 'VEG2'): 140,
('Mar', 'OIL1'): 130,
('Mar', 'OIL2'): 100,
('Mar', 'OIL3'): 95,
('Apr', 'VEG1'): 120,
('Apr', 'VEG2'): 110,
('Apr', 'OIL1'): 120,
('Apr', 'OIL2'): 120,
('Apr', 'OIL3'): 125,
('May', 'VEG1'): 100,
('May', 'VEG2'): 120,
('May', 'OIL1'): 150,
('May', 'OIL2'): 110,
('May', 'OIL3'): 105,
('Jun', 'VEG1'): 90,
('Jun', 'VEG2'): 100,
('Jun', 'OIL1'): 140,
('Jun', 'OIL2'): 80,
('Jun', 'OIL3'): 135
}
hardness = {"VEG1": 8.8, "VEG2": 6.1, "OIL1": 2.0, "OIL2": 4.2, "OIL3": 5.0}
price = 150
init_store = 500
target_store = 500
veg_cap = 200
oil_cap = 250
min_hardness = 3
max_hardness = 6
holding_cost = 5
food = lp.LpProblem(name='Food_Manufacture_I',sense=lp.LpMaximize)
# 每个月生产的油量
produce = lp.LpVariable.dicts(name="Produce",indexs=months,lowBound=0,cat='Continuous')
# 每个月购买油的量
buy = lp.LpVariable.dicts(name = "Buy",
indexs=[(m,o) for m in months for o in oils],
lowBound=0,
cat='Continuous')
# 每个月用掉的油的量
consume = lp.LpVariable.dicts( name = "Use",
indexs=[(m,o) for m in months for o in oils],
lowBound=0,
cat='Continuous')
# 每个月的存储量
store = lp.LpVariable.dicts( name = "Store",
indexs=[(m,o) for m in months for o in oils],
lowBound=0,
cat='Continuous')
#0. 目标函数
obj = lp.lpSum(price*produce[m] for m in months)-lp.lpSum(buy[m,o]*cost[m,o]+holding_cost*store[m,o] for m in months for o in oils)
food += obj
# 约束条件
#1. Initial Balance
for oil in oils:
food += init_store+buy[months[0],oil]==consume[months[0], oil] + store[months[0], oil]
#2. Balance
for oil in oils:
for month in months:
if month != month[0]:
food += store[months[months.index(month)-1], oil] + buy[month, oil]== consume[month, oil] + store[month, oil]
#3. Inventory Target
for oil in oils:
food += store[months[-1], oil] == target_store
#4.1 Vegetable Oil Capacity
for month in months:
food += lp.lpSum(consume[month, oil] for oil in oils if "VEG" in oil)<= veg_cap
#4.2 Non-vegetable Oil Capacity
for month in months:
food += lp.lpSum(consume[month, oil] for oil in oils if "OIL" in oil)<= oil_cap
#5. Hardness
for month in months:
food += lp.lpSum(hardness[oil]*consume[month, oil] for oil in oils)>= min_hardness*produce[month]
for month in months:
food += lp.lpSum(hardness[oil]*consume[month, oil] for oil in oils) <= max_hardness*produce[month]
#6. Mass Conservation
for month in months:
food += lp.lpSum(consume[month,oil] for oil in oils) == produce[month]
food.solve()
1
lp.value(food.objective)
107842.59259499997
rows = months.copy()
columns = oils.copy()
purchase_plan = pd.DataFrame(columns=columns, index=rows, data=0.0)
for month, oil in buy.keys():
if (abs(lp.value(buy[month, oil])) > 1e-6):
purchase_plan.loc[month, oil] = np.round(lp.value(buy[month, oil]), 1)
purchase_plan
VEG1 | VEG2 | OIL1 | OIL2 | OIL3 | |
---|---|---|---|---|---|
Jan | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
Feb | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
Mar | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
Apr | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
May | 0.0 | 0.0 | 0.0 | 0.0 | 750.0 |
Jun | 659.3 | 540.7 | 0.0 | 750.0 | 0.0 |
rows = months.copy()
columns = oils.copy()
reqs = pd.DataFrame(columns=columns, index=rows, data=0.0)
for month, oil in consume.keys():
if (abs(lp.value(consume[month, oil])) > 1e-6):
reqs.loc[month, oil] = np.round(lp.value(consume[month, oil]), 1)
reqs
VEG1 | VEG2 | OIL1 | OIL2 | OIL3 | |
---|---|---|---|---|---|
Jan | 159.3 | 40.7 | 0.0 | 250.0 | 0.0 |
Feb | 85.2 | 114.8 | 0.0 | 0.0 | 250.0 |
Mar | 85.2 | 114.8 | 0.0 | 0.0 | 250.0 |
Apr | 159.3 | 40.7 | 0.0 | 250.0 | 0.0 |
May | 11.1 | 188.9 | 0.0 | 0.0 | 250.0 |
Jun | 159.3 | 40.7 | 0.0 | 250.0 | 0.0 |
rows = months.copy()
columns = oils.copy()
store_plan = pd.DataFrame(columns=columns, index=rows, data=0.0)
for month, oil in store.keys():
if (abs(lp.value(store[month, oil])) > 1e-6):
store_plan.loc[month, oil] = np.round(lp.value(store[month, oil]), 1)
store_plan
VEG1 | VEG2 | OIL1 | OIL2 | OIL3 | |
---|---|---|---|---|---|
Jan | 340.7 | 459.3 | 500.0 | 250.0 | 500.0 |
Feb | 255.6 | 344.4 | 500.0 | 250.0 | 250.0 |
Mar | 170.4 | 229.6 | 500.0 | 250.0 | 0.0 |
Apr | 11.1 | 188.9 | 500.0 | 0.0 | 0.0 |
May | 0.0 | 0.0 | 500.0 | 0.0 | 500.0 |
Jun | 500.0 | 500.0 | 500.0 | 500.0 | 500.0 |